﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using AmiEmuLib.AmigaMachine.AmigaCPU;
using System.Xml;
using System.Diagnostics;
using TomanuExtensions;

namespace AmiEmuLib.SaveState
{
    /// <summary>
    /// Base cpu state.
    /// </summary>
    [DebuggerDisplay("{GetType().Name}, NothingChanged = {NothingChanged}, Prev = {Prev != null}")]
    public abstract class CPU680x0State
    {
        /// <summary>
        /// Cycle counter.
        /// </summary>
        public ulong CycleCounter { get; private set; }

        [DebuggerDisplay("{Hex.UIntToHex(D0)}")]
        public uint D0 { get; private set; }

        [DebuggerDisplay("{Hex.UIntToHex(D1)}")]
        public uint D1 { get; private set; }

        [DebuggerDisplay("{Hex.UIntToHex(D2)}")]
        public uint D2 { get; private set; }

        [DebuggerDisplay("{Hex.UIntToHex(D3)}")]
        public uint D3 { get; private set; }

        [DebuggerDisplay("{Hex.UIntToHex(D4)}")]
        public uint D4 { get; private set; }

        [DebuggerDisplay("{Hex.UIntToHex(D5)}")]
        public uint D5 { get; private set; }

        [DebuggerDisplay("{Hex.UIntToHex(D6)}")]
        public uint D6 { get; private set; }

        [DebuggerDisplay("{Hex.UIntToHex(D7)}")]
        public uint D7 { get; private set; }

        [DebuggerDisplay("{Hex.UIntToHex(A0)}")]
        public uint A0 { get; private set; }

        [DebuggerDisplay("{Hex.UIntToHex(A1)}")]
        public uint A1 { get; private set; }

        [DebuggerDisplay("{Hex.UIntToHex(A2)}")]
        public uint A2 { get; private set; }

        [DebuggerDisplay("{Hex.UIntToHex(A3)}")]
        public uint A3 { get; private set; }

        [DebuggerDisplay("{Hex.UIntToHex(A4)}")]
        public uint A4 { get; private set; }

        [DebuggerDisplay("{Hex.UIntToHex(A5)}")]
        public uint A5 { get; private set; }

        [DebuggerDisplay("{Hex.UIntToHex(A6)}")]
        public uint A6 { get; private set; }

        /// <summary>
        /// Stack pointer = A7.
        /// </summary>
        [DebuggerDisplay("{Hex.UIntToHex(SP)}")]
        public uint SP { get; private set; }

        /// <summary>
        /// Program counter.
        /// </summary>
        [DebuggerDisplay("{Hex.UIntToHex(PC)}")]
        public uint PC { get; private set; }

        /// <summary>
        /// Previous state.
        /// </summary>
        public readonly CPU680x0State Prev;

        /// <summary>
        /// Constructor.
        /// </summary>
        internal CPU680x0State()
        {
        }

        /// <summary>
        /// Constructor.
        /// </summary>
        /// <param name="a_cpu"></param>
        /// <param name="a_prev"></param>
        internal CPU680x0State(CPU680x0 a_cpu, CPU680x0State a_prev = null)
        {
            Prev = a_prev;

            CycleCounter = a_cpu.CycleCounter;

            D0 = a_cpu.D0;
            D1 = a_cpu.D1;
            D2 = a_cpu.D2;
            D3 = a_cpu.D3;
            D4 = a_cpu.D4;
            D5 = a_cpu.D5;
            D6 = a_cpu.D6;
            D7 = a_cpu.D7;

            A0 = a_cpu.A0;
            A1 = a_cpu.A1;
            A2 = a_cpu.A2;
            A3 = a_cpu.A3;
            A4 = a_cpu.A4;
            A5 = a_cpu.A5;
            A6 = a_cpu.A6;

            SP = a_cpu.SP;
            PC = a_cpu.PC;
        }

        /// <summary>
        /// Constructor.
        /// </summary>
        /// <param name="a_prev"></param>
        internal CPU680x0State(CPU680x0State a_prev)
        {
            Prev = a_prev;
            Restore(a_prev);
        }

        /// <summary>
        /// Restore cpu state.
        /// </summary>
        /// <param name="a_cpu"></param>
        internal virtual void Restore(CPU680x0 a_cpu)
        {
            a_cpu.CycleCounter = CycleCounter;

            a_cpu.D0 = D0;
            a_cpu.D1 = D1;
            a_cpu.D2 = D2;
            a_cpu.D3 = D3;
            a_cpu.D4 = D4;
            a_cpu.D5 = D5;
            a_cpu.D6 = D6;
            a_cpu.D7 = D7;

            a_cpu.A0 = A0;
            a_cpu.A1 = A1;
            a_cpu.A2 = A2;
            a_cpu.A3 = A3;
            a_cpu.A4 = A4;
            a_cpu.A5 = A5;
            a_cpu.A6 = A6;

            a_cpu.SP = SP;
            a_cpu.PC = PC;
        }

        /// <summary>
        /// Create cpu state.
        /// </summary>
        /// <param name="a_CPUBase"></param>
        /// <param name="a_prev"></param>
        /// <returns></returns>
        internal static CPU680x0State Create(CPU680x0 a_CPUBase, CPU680x0State a_prev = null)
        {
            if (a_CPUBase is CPU68000)
                return new CPU68000State((CPU68000)a_CPUBase, (CPU68000State)a_prev);
            else if (a_CPUBase is CPU68020)
                return new CPU68020State((CPU68020)a_CPUBase, (CPU68020State)a_prev);

            throw new ArgumentException();
        }

        /// <summary>
        /// Create cpu state.
        /// </summary>
        /// <param name="a_prev"></param>
        /// <returns></returns>
        internal static CPU680x0State Create(CPU680x0State a_prev)
        {
            if (a_prev is CPU68000State)
                return new CPU68000State(a_prev);
            else if (a_prev is CPU68020State)
                return new CPU68020State(a_prev);
            else
                throw new ArgumentException();
        }

        /// <summary>
        /// Restore state from another state.
        /// </summary>
        /// <param name="Prev"></param>
        protected virtual void Restore(CPU680x0State a_cpu)
        {
            CycleCounter = a_cpu.CycleCounter;

            D0 = a_cpu.D0;
            D1 = a_cpu.D1;
            D2 = a_cpu.D2;
            D3 = a_cpu.D3;
            D4 = a_cpu.D4;
            D5 = a_cpu.D5;
            D6 = a_cpu.D6;
            D7 = a_cpu.D7;

            A0 = a_cpu.A0;
            A1 = a_cpu.A1;
            A2 = a_cpu.A2;
            A3 = a_cpu.A3;
            A4 = a_cpu.A4;
            A5 = a_cpu.A5;
            A6 = a_cpu.A6;

            SP = a_cpu.SP;
            PC = a_cpu.PC;
        }

        public override bool Equals(object a_obj)
        {
            if (a_obj == null)
                return false;
            CPU680x0State cpu = a_obj as CPU680x0State;
            if (cpu == null)
                return false;

            return (CycleCounter == cpu.CycleCounter) &&
                   (D0 == cpu.D0) &&
                   (D1 == cpu.D1) &&
                   (D2 == cpu.D2) &&
                   (D3 == cpu.D3) &&
                   (D4 == cpu.D4) &&
                   (D5 == cpu.D5) &&
                   (D6 == cpu.D6) &&
                   (D7 == cpu.D7) &&
                   (A0 == cpu.A0) &&
                   (A1 == cpu.A1) &&
                   (A2 == cpu.A2) &&
                   (A3 == cpu.A3) &&
                   (A4 == cpu.A4) &&
                   (A5 == cpu.A5) &&
                   (A6 == cpu.A6) &&
                   (SP == cpu.SP) &&
                   (PC == cpu.PC);
        }

        public override int GetHashCode()
        {
            return CycleCounter.GetHashCode() ^
                   D0.GetHashCode() ^
                   D1.GetHashCode() ^
                   D2.GetHashCode() ^
                   D3.GetHashCode() ^
                   D4.GetHashCode() ^
                   D5.GetHashCode() ^
                   D6.GetHashCode() ^
                   D7.GetHashCode() ^
                   A0.GetHashCode() ^
                   A1.GetHashCode() ^
                   A2.GetHashCode() ^
                   A3.GetHashCode() ^
                   A4.GetHashCode() ^
                   A5.GetHashCode() ^
                   A6.GetHashCode() ^
                   SP.GetHashCode() ^
                   PC.GetHashCode();
        }

        /// <summary>
        /// Write xml.
        /// </summary>
        /// <param name="a_reader"></param>
        internal virtual void ReadXml(XmlReader a_reader)
        {
            if (a_reader.IsStartElement("A0"))
                A0 = a_reader.ReadElementContentAsHexUInt("A0");
            if (a_reader.IsStartElement("A1"))
                A1 = a_reader.ReadElementContentAsHexUInt("A1");
            if (a_reader.IsStartElement("A2"))
                A2 = a_reader.ReadElementContentAsHexUInt("A2");
            if (a_reader.IsStartElement("A3"))
                A3 = a_reader.ReadElementContentAsHexUInt("A3");
            if (a_reader.IsStartElement("A4"))
                A4 = a_reader.ReadElementContentAsHexUInt("A4");
            if (a_reader.IsStartElement("A5"))
                A5 = a_reader.ReadElementContentAsHexUInt("A5");
            if (a_reader.IsStartElement("A6"))
                A6 = a_reader.ReadElementContentAsHexUInt("A6");

            if (a_reader.IsStartElement("D0"))
                D0 = a_reader.ReadElementContentAsHexUInt("D0");
            if (a_reader.IsStartElement("D1"))
                D1 = a_reader.ReadElementContentAsHexUInt("D1");
            if (a_reader.IsStartElement("D2"))
                D2 = a_reader.ReadElementContentAsHexUInt("D2");
            if (a_reader.IsStartElement("D3"))
                D3 = a_reader.ReadElementContentAsHexUInt("D3");
            if (a_reader.IsStartElement("D4"))
                D4 = a_reader.ReadElementContentAsHexUInt("D4");
            if (a_reader.IsStartElement("D5"))
                D5 = a_reader.ReadElementContentAsHexUInt("D5");
            if (a_reader.IsStartElement("D6"))
                D6 = a_reader.ReadElementContentAsHexUInt("D6");
            if (a_reader.IsStartElement("D7"))
                D7 = a_reader.ReadElementContentAsHexUInt("D7");

            if (a_reader.IsStartElement("CycleCounter"))
                CycleCounter = a_reader.ReadElementContentAsULong("CycleCounter");

            if (a_reader.IsStartElement("SP"))
                SP = a_reader.ReadElementContentAsHexUInt("SP");
            if (a_reader.IsStartElement("PC"))
                PC = a_reader.ReadElementContentAsHexUInt("PC");
        }

        /// <summary>
        /// Write xml.
        /// </summary>
        /// <param name="a_writer"></param>
        internal virtual void WriteXml(XmlWriter a_writer)
        {
            if (((Prev != null) && (Prev.A0 != A0)) || (Prev == null))
                a_writer.WriteHexElement("A0", A0);
            if (((Prev != null) && (Prev.A1 != A1)) || (Prev == null))
                a_writer.WriteHexElement("A1", A1);
            if (((Prev != null) && (Prev.A2 != A2)) || (Prev == null))
                a_writer.WriteHexElement("A2", A2);
            if (((Prev != null) && (Prev.A3 != A3)) || (Prev == null))
                a_writer.WriteHexElement("A3", A3);
            if (((Prev != null) && (Prev.A4 != A4)) || (Prev == null))
                a_writer.WriteHexElement("A4", A4);
            if (((Prev != null) && (Prev.A5 != A5)) || (Prev == null))
                a_writer.WriteHexElement("A5", A5);
            if (((Prev != null) && (Prev.A6 != A6)) || (Prev == null))
                a_writer.WriteHexElement("A6", A6);

            if (((Prev != null) && (Prev.D0 != D0)) || (Prev == null))
                a_writer.WriteHexElement("D0", D0);
            if (((Prev != null) && (Prev.D1 != D1)) || (Prev == null))
                a_writer.WriteHexElement("D1", D1);
            if (((Prev != null) && (Prev.D2 != D2)) || (Prev == null))
                a_writer.WriteHexElement("D2", D2);
            if (((Prev != null) && (Prev.D3 != D3)) || (Prev == null))
                a_writer.WriteHexElement("D3", D3);
            if (((Prev != null) && (Prev.D4 != D4)) || (Prev == null))
                a_writer.WriteHexElement("D4", D4);
            if (((Prev != null) && (Prev.D5 != D5)) || (Prev == null))
                a_writer.WriteHexElement("D5", D5);
            if (((Prev != null) && (Prev.D6 != D6)) || (Prev == null))
                a_writer.WriteHexElement("D6", D6);
            if (((Prev != null) && (Prev.D7 != D7)) || (Prev == null))
                a_writer.WriteHexElement("D7", D7);

            if (((Prev != null) && (Prev.CycleCounter != CycleCounter)) || (Prev == null))
                a_writer.WriteElement("CycleCounter", CycleCounter);

            if (((Prev != null) && (Prev.SP != SP)) || (Prev == null))
                a_writer.WriteHexElement("SP", SP);
            if (((Prev != null) && (Prev.PC != PC)) || (Prev == null))
                a_writer.WriteHexElement("PC", PC);
        }

        public bool NothingChanged
        {
            get
            {
                return this.Equals(Prev);
            }
        }
    }
}